package net.trevize.galatee;

import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.net.SocketTimeoutException;
import java.net.URI;
import java.util.Calendar;
import java.util.Vector;

import org.apache.commons.httpclient.DefaultHttpMethodRetryHandler;
import org.apache.commons.httpclient.Header;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.params.HttpMethodParams;
import org.apache.commons.vfs.FileObject;
import org.apache.commons.vfs.FileSystemException;
import org.apache.commons.vfs.impl.StandardFileSystemManager;

/**
 * GItem is the POJO object for an item in a Galatee panel.
 * 
 * @author Nicolas James <nicolas.james@gmail.com> [[http://njames.trevize.net]]
 * GItem.java - May 12, 2009
 */

public class GItem {

	private URI uri;

	private String filename;

	private String local_filepath;

	private BufferedImage image;

	private String text;

	private Vector<Object> data;

	public GItem(URI uri) {
		this.uri = uri;

		//get the filename.
		String uri_string = uri.toString();
		filename = uri_string.substring(uri_string.lastIndexOf("/") + 1,
				uri_string.length());

		this.image = null;

		if (uri.getScheme().equals("file")) {
			local_filepath = uri.getPath();
		}

		if (uri.getScheme().equals("tar")) {
			local_filepath = uri.toString();
		}
	}

	public String getLocalFilepath() {
		if (local_filepath == null && uri.getScheme().equals("http")) {
			//download the image.
			String image_filename = ""
					+ Calendar.getInstance().getTimeInMillis() + "_"
					+ (int) (Math.random() * 1000) + "_" + filename;

			if (downloadWWWResource(uri.toString(), GalateeProperties
					.getTemporary_directory()
					+ File.separator + image_filename)) {
				local_filepath = GalateeProperties.getTemporary_directory()
						+ File.separator + image_filename;
			}
		}
		return local_filepath;
	}

	public InputStream getInputStream() {
		InputStream is = null;

		if (uri.getScheme().equals("tar")) {
			StandardFileSystemManager mgr = new StandardFileSystemManager();
			try {
				mgr.init();
			} catch (FileSystemException e2) {
				e2.printStackTrace();
			}

			//get the file object.
			FileObject file_object = null;
			try {
				file_object = mgr.resolveFile(getLocalFilepath());
			} catch (FileSystemException e) {
				e.printStackTrace();
			}

			//read in the input stream of the file object exactly how the size of the file is.
			//else i experience a problem (make a read operation on the read give data but not **consume** the stream). 
			long file_size = 0;
			try {
				file_size = file_object.getContent().getSize();
			} catch (FileSystemException e1) {
				e1.printStackTrace();
			}

			PipedInputStream pis = new PipedInputStream((int) file_size);
			PipedOutputStream pos = null;
			try {
				pos = new PipedOutputStream(pis);
			} catch (IOException e) {
				e.printStackTrace();
			}

			try {
				InputStream file_object_is = file_object.getContent()
						.getInputStream();
				int buffer_size = 1024;
				byte[] buffer = new byte[buffer_size];
				int n = 0;
				while (file_size > 0) {
					file_object_is.read(buffer);
					if (file_size - buffer_size >= 0) {
						n = buffer_size;
					} else {
						n = (int) file_size;
					}
					pos.write(buffer, 0, n);
					file_size = file_size - n;
				}
				file_object_is.close();
				file_object.close();
				mgr.freeUnusedResources();
				mgr.close();
				pos.close();
			} catch (FileSystemException e) {
				e.printStackTrace();
			} catch (IOException e) {
				e.printStackTrace();
			}

			is = pis;
		} else {
			try {
				is = new FileInputStream(getLocalFilepath());
			} catch (FileNotFoundException e) {
				e.printStackTrace();
			}
		}

		return is;
	}

	private boolean downloadWWWResource(String urlvalue, String filepath) {
		try {
			// Create an instance of HttpClient.
			HttpClient client = new HttpClient();

			// Create a method instance.
			GetMethod method = new GetMethod(urlvalue);

			// Provide custom retry handler is necessary.
			method.getParams().setParameter(HttpMethodParams.RETRY_HANDLER,
					new DefaultHttpMethodRetryHandler(1, false));

			// set a timeout of each method
			method.getParams().setParameter(HttpMethodParams.SO_TIMEOUT, 5000);

			//change user-agent (cause of some website which reply 403 if user-agent unknown).
			Header ua = new Header("User-Agent", "Mozilla");
			method.setRequestHeader(ua);

			// Execute the method.
			int statusCode = client.executeMethod(method);

			//if the status code of the response is not OK return false
			if (statusCode != HttpStatus.SC_OK) {
				System.err.println("Method failed: " + method.getStatusLine()
						+ " for URL " + urlvalue);
				return false;
			}

			// save the response
			InputStream is = method.getResponseBodyAsStream();
			FileOutputStream fos = new FileOutputStream(new File(filepath));

			byte[] buf = new byte[4096];
			int n;
			while ((n = is.read(buf)) > 0) {
				fos.write(buf, 0, n);
			}

			//flush and close the stream.
			fos.flush();
			fos.close();
		} catch (HttpException e) {
			e.printStackTrace();
			System.err.println("HttpException for URL " + urlvalue);
			return false;
		} catch (FileNotFoundException e) {
			e.printStackTrace();
			System.err.println("FileNotFoundException for URL " + urlvalue);
			return false;
		} catch (SocketTimeoutException e) {
			e.printStackTrace();
			System.err.println("SocketTimeoutException for URL " + urlvalue);
			return false;
		} catch (IOException e) {
			e.printStackTrace();
			System.err.println("IOException for URL " + urlvalue);
			return false;
		} catch (Exception e) {
			e.printStackTrace();
			System.err.println("Exception for URL " + urlvalue);
			return false;
		}

		return true;
	}

	public String getFilename() {
		return filename;
	}

	public void setFilename(String filename) {
		this.filename = filename;
	}

	public BufferedImage getImage() {
		return image;
	}

	public void setImage(BufferedImage image) {
		this.image = image;
	}

	public String getText() {
		return text;
	}

	public void setText(String text) {
		this.text = text;
	}

	public Vector<Object> getData() {
		return data;
	}

	public void setData(Vector<Object> data) {
		this.data = data;
	}

	public URI getUri() {
		return uri;
	}

	public void setUri(URI uri) {
		this.uri = uri;
	}

}
